#include "REString.h"

static std::tr1::shared_ptr<RE_StringTable> m_pTable;
bool RE_String::operator==(const RE_String & str) const
{
	if(m_id == str.m_id)
	{
		return true;
	}
	return false;
}
bool RE_String::operator<(const RE_String & str) const
{
	if(m_id < str.m_id)
	{
		return true;
	}
	return false;
}
RE_String & RE_String::operator=(RE_String &str)
{
	if(m_id != str.m_id)
	{
		RE_StringTable::Get()->SubReference(m_id);
		m_id = str.m_id;
	}
	RE_StringTable::Get()->AddReference(m_id);
#ifdef _DEBUG
#ifdef _TESTMODE
	str_ = str.str_;
#endif
#endif
	return *this;
}
RE_String::RE_String(const RE_String & str)
{
	if(m_id != str.m_id)
	{
		RE_StringTable::Get()->SubReference(m_id);
		m_id = str.m_id;
	}
	RE_StringTable::Get()->AddReference(m_id);
#ifdef _DEBUG
		#ifdef _TESTMODE
	str_ = str.str_;
#endif
#endif
}
RE_String::RE_String(const std::string & str)
{
	m_id = RE_StringTable::Get()->GetIdByString(str);
	RE_StringTable::Get()->AddReference(m_id);
#ifdef _DEBUG
		#ifdef _TESTMODE
	str_ = *RE_StringTable::Get()->GetStringById(m_id);
#endif
#endif
}
RE_String::RE_String(char * c_str)
{
	m_id = RE_StringTable::Get()->GetIdByString(std::string(c_str));
	RE_StringTable::Get()->AddReference(m_id);
#ifdef _DEBUG
		#ifdef _TESTMODE
	str_ = *RE_StringTable::Get()->GetStringById(m_id);
#endif
#endif
}

RE_String::RE_String(unsigned int id)
{
	m_id = RE_StringTable::Get()->ValidateId(id);
	RE_StringTable::Get()->AddReference(m_id);
#ifdef _DEBUG
		#ifdef _TESTMODE
	str_ = *RE_StringTable::Get()->GetStringById(m_id);
#endif
#endif
}


RE_String::RE_String(const std::wstring & str)
{
	std::string lookup_str;
	lookup_str.append(str.begin(), str.end());
	m_id = RE_StringTable::Get()->GetIdByString(lookup_str);
	RE_StringTable::Get()->AddReference(m_id);
#ifdef _DEBUG
		#ifdef _TESTMODE
	str_ = *RE_StringTable::Get()->GetStringById(m_id);
#endif
#endif
}
RE_String::RE_String(const TCHAR* const str)
{
	std::string lookup_str; std::wstring wstr = str;
	lookup_str.append(wstr.begin(), wstr.end());
	m_id = RE_StringTable::Get()->GetIdByString(lookup_str);
	RE_StringTable::Get()->AddReference(m_id);
#ifdef _DEBUG
		#ifdef _TESTMODE
	str_ = *RE_StringTable::Get()->GetStringById(m_id);
#endif
#endif
}


RE_String::~RE_String()
{
	RE_StringTable::Get()->SubReference(m_id);
}

void RE_StringTable::AddReference(const unsigned int id)
{
	if(id == 0)
	{
		return;
	}
	StringMap::iterator i = m_Strings.find(id);
	if(i == m_Strings.end())
	{
		return;
	}
	++(*i).second.second; // add +1 to ref count
}
void RE_StringTable::SubReference(const unsigned int id)
{
	if(id == 0)
	{
		return;
	}
	StringMap::iterator i = m_Strings.find(id);
	if(i == m_Strings.end())
	{
		return;
	}
	--(*i).second.second; // remove 1 from ref count
	if((*i).second.second == 0) // if there are no references, remove the string from the map and add the id to the factory for later use"
	{
		GetFactory()->AddFreeId();
		m_Strings.erase(i);
	}
}


unsigned int RE_StringTable::GetIdByString(const std::string & str)
{
	StringMap::iterator i = m_Strings.begin();
	StringMap::iterator end = m_Strings.end();
	while(i != end)
	{
		if(i->second.first == str)
		{
			return i->first;
		}
		++i;
	}
	// this string is here for first time. Create an id and put in in the map
	int id = GetFactory()->CreateStringId();
	m_Strings[ id ] = std::make_pair(str, 0);
	return id;
}



RE_StringTable * RE_StringTable::Get()
{
	if( m_pTable == std::tr1::shared_ptr<RE_StringTable>())
	{
		m_pTable = std::tr1::shared_ptr<RE_StringTable>(RE_NEW RE_StringTable);
	}
	return m_pTable.get();
}
std::tr1::shared_ptr<RE_StringTable>* RE_StringTable::GetPtr()
{
	return &m_pTable;
}
RE_StringTable::RE_StringTable()
{
}
void RE_StringTable::ReleaseStrings()
{
	m_Strings.clear();
}
RE_StringTable::~RE_StringTable()
{

}


unsigned int RE_StringTable::ValidateId(unsigned int id)
{
	StringMap::iterator i = m_Strings.find(id);
	if(i == m_Strings.end())
	{
		return 0;
	}
	return i->first;
}

unsigned int RE_StringFactory::CreateStringId()
{
	if(m_free_ids == 0)
	{
		return ++m_last_id;
	} else {
		//there are free ids somewhere in the map. Loop and find one
		StringMap::iterator i = RE_StringTable::Get()->m_Strings.begin();
		StringMap::iterator end = RE_StringTable::Get()->m_Strings.end();
		unsigned int id = 1;
		while(i != end)
		{
			if((*i).first != id)
			{
				SubFreeId();
				return id;
			}
			++id;
		}
		return id;
	}
	return 0;
}

const std::string * RE_StringTable::GetStringById(unsigned int id) // forced to use poitner over reference. Cannot return flag if error
{
	StringMap::const_iterator i = m_Strings.find(id);
	if(i == m_Strings.end())
	{
		return NULL;
	}
	return &i->second.first;
}
const char * const RE_String::c_str() const
{
	return RE_StringTable::Get()->GetStringById(m_id)->c_str();
}






RE_StringFactory * RE_StringTable::GetFactory()
{
	if(m_pFactory == std::tr1::shared_ptr<RE_StringFactory>())
		m_pFactory = std::tr1::shared_ptr<RE_StringFactory>(RE_NEW RE_StringFactory);
	return m_pFactory.get();
}